<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script>
        /*
            this调用的几种情况：
                1. 方法调用模式：this指向调用它所在方法的对象【与所在方法的调用位置有关，与声明位置无关】
                2. 函数调用模式：window 调用方法没有明确对象的时候 【如匿名函数，setTimeout】
                3. 构造函数调用模式：构造的实例对象
                4. apply call bind：第一个参数
                5. 箭头函数：声明时绑定的this，声明时的上下文
                6. 严格模式：this没有被执行环境定义，this指向undefined
                7. Class类里面的指向：
                    a. constructor() 中的this: 指向创建的实例对象
                    b. 类中方法的this: 指向调用者
                8. HTML事件函数（不能是箭头函数）:  指向接受事件的HTML元素
        */ 
//-------------------------------------------------------------------------------------------------------------------------------------------
        //1. 方法调用模式：（1）this 指向调用它所在方法的对象；（2）this和声明位置无关，和调用位置有关
        // eg1. 
        // 声明位置
        var test = function() {
            console.log(this.x);
        };

        var x = '2';

        var obj = {
            x: '1',
            fn: test,
        };

        // 调用位置
        test();  // 2  test()在全局作用域下调用 this指向window
        obj.fn();  // 1 obj.fn() 相当于是test()  此时test()中的this指向obj

        // eg2. 
        let obj1 = {
            a: 222,
        };

        let obj2 = {
            a: 111,
            fn: function() {   // fn 在obj2内部声明
                console.log(this.a);
            },
        };

        obj1.fn = obj2.fn;
        obj1.fn();   // 222   fn 在obj1内部调用 this指向obj1

//-------------------------------------------------------------------------------------------------------------------------------------------
        // 2. 函数调用模式: this 指向window
        // eg1. 
        var a = 1;
        function fn1() {
            console.log(this.a);
        };

        fn1(); // 1

        window.b = 2;
        function fn2() {
            console.log(this.b);
        };

        fn2();  // 1 

        // eg2. 匿名函数 setTimeout
        (function() {
            console.log(this);   // window
        })();

        setTimeout(() => {
            console.log(this); // window
        }, 1000);

        setTimeout(function() {
            console.log(this);  // window
        }, 1000);

//-------------------------------------------------------------------------------------------------------------------------------------------
        // 3. 构造函数调用模式: 实例对象
        function Person(name, age) {
            console.log(this);   // p  与new原理有关
            this.name = name;
            this.age = age;
        };

        var p = new Person('Jack', 18);

//-------------------------------------------------------------------------------------------------------------------------------------------
        // 4. apply call bind
        var obj_1 = {
            name: '111',
            getName: function() {
                console.log('this', this);
                console.log(this.name);
            },
        };

        var otherObj = {
            name: '222',
        };

        var name = '333';

        obj_1.getName();    // 111  方法调用模式下，this指向调用这个方法的对象
        obj_1.getName.call();  // 333 此时call尝试修改this指向，但他没有传参 所以this指向window
        obj_1.getName.call(otherObj); // 222  call修改了this指向
        // apply bind 同理 不赘述

//-------------------------------------------------------------------------------------------------------------------------------------------
        // 5. 箭头函数
        /*
            判断箭头函数的 this：
            技巧：它的外层没有函数，this 是 window；外层有函数，看外层函数的 this 是谁，它的 this 就是谁
        */

        // eg1:
        let obj_2={
            a:222,
            fn:function(){    
                setTimeout(()=>{console.log(this.a)});
            }
        };
        obj_2.fn(); // 222 

        // eg2:
        var name = 'window'; 
        var A = {
            name: 'A',
            sayHello: () => {
                console.log(this.name)
            }
        };

        A.sayHello(); // 输出的是window,根据刚才讲的规则就可以判断

        // 那如何改造成永远绑定A呢：

        var name = 'window'; 
        var A = {
        name: 'A',
        sayHello: function(){
            var s = () => console.log(this.name)
            return s//返回箭头函数s
        }
        }

        var sayHello = A.sayHello();
        sayHello();// 输出A 

//-------------------------------------------------------------------------------------------------------------------------------------------
        // 6. 严格模式：非严格模式this 默认指向全局对象 window，严格模式this 为undefined
        // eg1. 非严格模式
        function f1(){
            return this;
        }
        console.log(f1() === window); // true

        // eg2. 严格模式
        "use strict";
        var fn2 = function(){
            return this
        }    
        console.log(fn2() == undefined); // true

    </script>
</body>
</html>